#pragma once

#include <iostream>
#include <string>
#include <functional>
#include <strings.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
#include <unordered_map>

#include "protocol.hpp"

using namespace std;

namespace Server
{
    enum
    {
        USAGE_ERROR = 1,
        SOCKET_ERROR,
        BIND_ERROR,
        LISTEN_ERROR
    };

    static const uint16_t g_port = 3000; // 服务器要开放的端口
    static const int g_backlog = 5;
    static const int g_num = 4096;

    using func_t = function<bool(const httpRequest &, httpResponse &)>;

    class httpServer
    {
    public:
        httpServer(func_t func, const uint16_t port = g_port) : _port(port), _listen_sockfd(-1), _func(func)
        {
        }
        ~httpServer() {}

        void init()
        {
            // 1. 创建套接字
            _listen_sockfd = socket(AF_INET, SOCK_STREAM, 0); // TCP通信
            if (_listen_sockfd == -1)
            {
                std::cerr << "create socket fail" << std::endl;
                exit(SOCKET_ERROR);
            }
            std::cout << "create socket success" << std::endl;

            // 2. bind网络信息
            struct sockaddr_in local;
            bzero(&local, 0);

            local.sin_family = AF_INET;
            local.sin_addr.s_addr = INADDR_ANY;
            local.sin_port = htons(_port);
            /*
            只有创建套接字，绑定这里才需要做主机转网络操作，其他例如recvfrom、sendto时不需要，因为：
            recvfrom和sendto都是系统调用，对于系统调用，用户不必知道是怎样实现的，系统调用自己在内部就会完成主机序列和网络序列的转换。
            而bind的参数是结构体， 绑定的IP,PORT等信息是用户自己手动设置的数据，所有在进行网络通信前，要进行主机序列转网络序列
            */

            int n = bind(_listen_sockfd, (struct sockaddr *)&local, sizeof(local));
            if (n == -1)
            {
                std::cerr << "bind fail" << std::endl;

                exit(BIND_ERROR);
            }
            std::cout << "bind success" << std::endl;

            // 3. TCP面向连接——通信前先建立连接——设置socket为监听状态
            n = listen(_listen_sockfd, g_backlog); // TODO
            if (n == -1)
            {
                std::cerr << "listen fail" << std::endl;
                exit(LISTEN_ERROR);
            }
            std::cout << "listen success and get a new link" << std::endl;
            
        }
        void start()
        {
            /* 服务器一经启动，应该是不受用户登录退出的影响，这样的进程就是守护进程 */

            while (true)
            {
                // 4. server端获取新连接
                struct sockaddr_in from;
                bzero(&from, 0);
                socklen_t fromlen = sizeof(from);

                // sockfd，未来和客户端进行通信的套接字fd
                int sockfd = accept(_listen_sockfd, (struct sockaddr *)&from, &fromlen);
                if (sockfd == -1)
                {
                    continue; // 当前服务错误，不影响运行
                }

                // 5. 通信
                pid_t id = fork();
                if (id == 0)
                {
                    // child process
                    close(_listen_sockfd); // 提供服务，不需要监听
                    if (fork() > 0)
                        exit(0);

                    handler(sockfd, _func);

                    close(sockfd);

                    exit(0);
                }
                // father process
                close(sockfd);

                waitpid(id, nullptr, 0);

                // version 2 end
            }
        }

        // void registercb(const std::string serviceName, func_t callback)
        // {
        //     _funcs.insert(std::make_pair(serviceName, callback));
        // }

    private:
        void handler(int sockfd, func_t func)
        {
            char buffer[g_num];
            httpRequest req;
            httpResponse resp;
            size_t n = recv(sockfd, buffer, sizeof(buffer) - 1, 0); // 假定能够一次性读取到完整的请求
            if (n > 0)
            {
                buffer[n] = 0; // 当作字符串处理
                req._inBuffer = buffer;
                req.parse();
                func(req, resp);
                // _funcs[req._path](req, resp);// 根据要访问的资源指定相对应的服务
                send(sockfd, resp._outBuffer.c_str(), resp._outBuffer.size(), 0);
            }

            // 1. 能读到完整的http请求

            // 2. 对请求做反序列化

            // 3. 得到http响应

            // 4. 对响应序列化

            // 5. 回送给客户端
        }

    private:
        int _listen_sockfd;
        uint16_t _port;
        func_t _func;

        // std::unordered_map<std::string, func_t> _funcs;
    };
}